package com.kun.sync.reader;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Map;

import com.kun.sync.util.DatabaseConnection;
import com.sun.rowset.CachedRowSetImpl;

/**
 * 数据源读取
 * 
 * @author zjkorder (2018/1/29 9:05)
 */
public class SourceReader implements ReaderInterface {

	/**
	 * 数据库连接配置
	 */
	private String[] databaseConnectionConfig;

	/**
	 * 同步表格名称
	 */
	private String tableName;

	/**
	 * where
	 */
	private String where;

	/**
	 * where params
	 */
	private Object[] params;

	/**
	 * 排序字段
	 */
	private Map<String, String> orderKey;

	/**
	 * 分页参数名称
	 */
	private String[] pagingParamsName;

	/**
	 * 分页参数名称
	 */
	private Object[] pagingParams;

	/**
	 * 每页大小
	 */
	private int limit;

	/**
	 * 数据库连接
	 */
	private Connection connection;

	/**
	 * 查询 statement
	 */
	private PreparedStatement queryStatement;

	/**
	 * 第一页SQL
	 */
	private String firstSql;

	/**
	 * 分页SQL
	 */
	private String pagingSql;

	/**
	 * 当前页码
	 */
	private int pagNum = 1;

	/**
	 * 是否还有数据
	 */
	private boolean next = true;

	/**
	 * 初始化数据读取
	 * 
	 * @param databaseConnectionConfig 数据库连接配置
	 * @param tableName 查询表格名称
	 * @param limit 分页大小
	 * @throws SQLException 初始化连接失败
	 */
	public SourceReader(String[] databaseConnectionConfig, String tableName, String where, Object[] params,
			Map<String, String> orderKey, int limit) throws SQLException {
		this.databaseConnectionConfig = databaseConnectionConfig;
		this.tableName = tableName;
		this.where = where;
		this.params = params;
		this.orderKey = orderKey;
		this.limit = limit;
		init();
	}

	/**
	 * 初始化读取
	 * 
	 * @throws SQLException 初始化失败
	 */
	private void init() throws SQLException {
		{
			// 初始化连接
			this.connection = DatabaseConnection.createConnection(databaseConnectionConfig);
		}

		String orderSql = null;
		{
			if (orderKey != null && !orderKey.isEmpty()) {
				StringBuilder orderBy = new StringBuilder("order by");
				for (String key : orderKey.keySet()) {
					orderBy.append(" ,").append(key).append(" ").append(orderKey.get(key));
				}
				orderSql = orderBy.toString().replaceFirst(",", "");
			}
		}

		{
			// 查询SQL
			firstSql = "select *  from " + tableName + (where == null ? " " : (" where " + where))
					+ (orderSql == null ? "" : orderSql) + " limit " + limit;

			if (orderSql == null) {
				pagingSql = firstSql + " offset ? ";
			} else {
				LinkedList<String> pagingParams = new LinkedList<>();
				StringBuilder pagingWhere = new StringBuilder("(");
				String[] keyArray = orderKey.keySet().toArray(new String[orderKey.size()]);
				for (int i = 1; i <= keyArray.length; i++) {
					String[] currentKeyArray = Arrays.copyOfRange(keyArray, 0, i);
					pagingWhere.append(i == 1 ? "(" : " or (");
					for (int j = 0; j < currentKeyArray.length; j++) {
						pagingParams.add(currentKeyArray[j]);
						pagingWhere.append(j == 0 ? "" : " and ");
						pagingWhere.append(currentKeyArray[j]).append(" ");
						if (j == currentKeyArray.length - 1) {
							pagingWhere.append(
									"desc".equalsIgnoreCase(orderKey.get(currentKeyArray[j]).toLowerCase()) ? "<"
											: ">");
						} else {
							pagingWhere.append("=");
						}
						pagingWhere.append("=").append("?");
					}
					pagingWhere.append(")");
				}
				pagingWhere.append(")");

				pagingParamsName = pagingParams.toArray(new String[pagingParams.size()]);
				pagingSql = "select *  from " + tableName + "where " + where + " and " + pagingWhere.toString()
						+ orderSql + " limit " + limit;
			}
		}

	}

	/**
	 * 获取下一页内容
	 * 
	 * @return PreparedStatement 已添加过参数
	 * @throws SQLException sql 执行异常
	 */
	public ResultSet next() throws SQLException {
		if (!next) {
			System.err.println("读取");
			return null;
		}
		System.out.println("读取");
		ResultSet resultSet = null;
		if (queryStatement == null) {
			queryStatement = connection.prepareStatement(firstSql);
			setParams(queryStatement, params);
		} else {
			queryStatement = connection.prepareStatement(pagingSql);
			setParams(queryStatement, mergeArray(params, pagingParams));
		}

		// 查询结果 并 关闭
		resultSet = queryStatement.executeQuery();
		CachedRowSetImpl cachedRowSet = new CachedRowSetImpl();
		cachedRowSet.populate(resultSet);
		DatabaseConnection.close(null, queryStatement, resultSet);

		{
			if (!cachedRowSet.next()) {
				next = false;
				return null;
			} else {
				cachedRowSet.last();
			}

			// 下一页结果查询
			if (orderKey == null || orderKey.isEmpty()) {
				pagNum++;
				pagingParams = new Object[] { (pagNum - 1) * limit };
			} else {
				for (int i = 0; i < pagingParamsName.length; i++) {
					pagingParams[i] = cachedRowSet.getObject(pagingParamsName[i]);
				}

			}
			cachedRowSet.beforeFirst();
		}
		return cachedRowSet;
	}

	/**
	 * 合并数据
	 * 
	 * @param a 数组a
	 * @param b 数组a
	 * @param <T> 数组类型
	 * @return 合并结果
	 */
	private <T> T[] mergeArray(T[] a, T[] b) {
		if (a == null && b == null) {
			return null;
		}
		if (a == null) {
			return b;
		}
		if (b == null) {
			return a;
		}
		T[] result = Arrays.copyOf(a, a.length + b.length);
		for (int i = a.length; i < a.length + b.length; i++) {
			result[i] = b[i - a.length];
		}
		return result;
	}

	/**
	 * 参数设置异常
	 * 
	 * @param prepareStatement prepareStatement
	 * @param params params
	 * @param sqlType sqlType
	 * @throws SQLException 参数设置异常
	 */
	private void setParams(PreparedStatement prepareStatement, Object[] params, int[] sqlType) throws SQLException {
		if (params != null)
			for (int i = 0; i < params.length; i++)
				prepareStatement.setObject(i + 1, params[i], sqlType[i]);
	}

	/**
	 * 参数设置异常
	 *
	 * @param prepareStatement prepareStatement
	 * @param params params
	 * @throws SQLException 参数设置异常
	 */
	private void setParams(PreparedStatement prepareStatement, Object[] params) throws SQLException {
		if (params != null)
			for (int i = 0; i < params.length; i++)
				prepareStatement.setObject(i + 1, params[i]);
	}

	/**
	 * 关闭数据连接
	 * 
	 * @throws SQLException 关闭异常
	 */
	public void close() throws SQLException {
		System.out.println("关闭");
		DatabaseConnection.close(connection, null, null);
	}

}
